package com.zhangxu.microservice.common.utils;

import com.zhangxu.microservice.common.exception.BizException;

import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class ListUtils {

    private ListUtils() {
    }

    public static <T, S> List<T> sortBy(List<T> list, List<S> temp, Function<T, S> mapper) {

        if (null == list || null == temp || null == mapper) {
            return list;
        }
        return list.stream().sorted(Comparator.comparingInt(t -> {
            int index = temp.indexOf(mapper.apply(t));
            if (index < 0) {
                return Integer.MAX_VALUE;
            } else {
                return index;
            }
        })).collect(Collectors.toList());
    }

    public static <K, V> Map<K, V> toMap(Collection<V> list, Function<V, K> keyMapper) {

        if (null == list || null == keyMapper) {
            return new HashMap<>(0);
        }
        return list.stream().collect(Collectors.toMap(keyMapper, Function.identity()));
    }

    public static <K, V> Map<K, V> toMapWithFilter(Collection<V> list, Function<V, K> keyMapper, Predicate<V> filter) {
        if (null == list || null == keyMapper) {
            return new HashMap<>(0);
        }

        if (null == filter) {
            return toMap(list, keyMapper);
        }

        return list.stream().filter(filter).collect(Collectors.toMap(keyMapper, Function.identity()));
    }

    public static <K, V> Map<K, V> toMap(Collection<V> list, Function<V, K> keyMapper, BinaryOperator<V> mergeFunction) {
        if (null == list || null == keyMapper) {
            return new HashMap<>(0);
        }

        if (null == mergeFunction) {
            toMap(list, keyMapper);
        }

        return list.stream().collect(Collectors.toMap(keyMapper, Function.identity(), mergeFunction));
    }

    public static <K, V> Map<K, V> toMap(Collection<V> list, Function<V, K> keyMapper, BinaryOperator<V> mergeFunction, Predicate<V> filter) {
        if (null == list || null == keyMapper) {
            return new HashMap<>(0);
        }

        if (null == mergeFunction) {
            throw new BizException("mergeFunction is null");
        }

        if (null == filter) {
            throw new BizException("filter is null");
        }

        return list.stream().filter(filter).collect(Collectors.toMap(keyMapper, Function.identity(), mergeFunction));
    }

    public static <K, V, E> Map<K, V> toMap(Collection<E> list, Function<E, K> keyMapper, Function<E, V> valueMapper) {

        if (null == list || null == keyMapper) {
            return new HashMap<>(0);
        }
        return list.stream().collect(Collectors.toMap(keyMapper, valueMapper));
    }

    public static <K, V, E> Map<K, V> toMap(Collection<E> list, Function<E, K> keyMapper, Function<E, V> valueMapper, BinaryOperator<V> mergeFunction) {
        if (null == list || null == keyMapper || null == valueMapper) {
            return new HashMap<>(0);
        }

        if (null == mergeFunction) {
            return toMap(list, keyMapper, valueMapper);
        }

        return list.stream().collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction));
    }

    public static <K, V> List<K> mapList(Collection<V> list, Function<V, K> mapper) {
        if (null == list || null == mapper) {
            return new ArrayList<>();
        }
        return list.stream().map(mapper).collect(Collectors.toList());
    }

    public static <K, V> List<K> mapDistinctList(Collection<V> list, Function<V, K> mapper) {
        if (null == list || null == mapper) {
            return new ArrayList<>();
        }
        return list.stream().map(mapper).distinct().collect(Collectors.toList());
    }


    public static <K, V> List<K> mapList(Collection<V> list, Function<V, K> mapper, Predicate<V> filter) {
        if (null == list || null == mapper) {
            return new ArrayList<>();
        }
        return list.stream().filter(filter).map(mapper).collect(Collectors.toList());
    }

    public static <K, V> Set<K> mapSet(List<V> list, Function<V, K> mapper) {
        if (null == list || null == mapper) {
            return new HashSet<>();
        }
        return list.stream().map(mapper).collect(Collectors.toSet());
    }

    public static <K, V> Set<K> mapSet(Collection<V> list, Function<V, K> mapper, Predicate<V> filter) {
        if (null == list || null == mapper) {
            return new HashSet<>();
        }
        return list.stream().filter(filter).map(mapper).collect(Collectors.toSet());
    }


    public static <E, K> Map<K, List<E>> group(Collection<E> list, Function<E, K> keyMapper) {
        return group(list, keyMapper, e -> e);
    }

    public static <E, K, V> Map<K, List<V>> group(Collection<E> list, Function<E, K> keyMapper, Function<E, V> valueMapper) {
        if (null == list || null == keyMapper || null == valueMapper) {
            return new HashMap<>(0);
        }
        return list.stream().collect(Collectors.groupingBy(keyMapper, Collectors.mapping(valueMapper, Collectors.toList())));
    }

    public static <E, K, V> Map<K, List<V>> group(Collection<E> list, Function<E, K> keyMapper, Function<E, V> valueMapper, Predicate<E> filter) {
        if (null == list || null == keyMapper || null == valueMapper) {
            return new HashMap<>(0);
        }

        if (null == filter) {
            return group(list, keyMapper, valueMapper);
        }

        return list.stream().filter(filter).collect(Collectors.groupingBy(keyMapper, Collectors.mapping(valueMapper, Collectors.toList())));
    }

}
